﻿#include "OiPCH.hpp"
#include "OiLT360CameraDriver.hpp"
#include <QtConcurrent/QtConcurrent>
#include "Core/OiCalib3D.hpp"

namespace Oi
{
    namespace PLScannerCore
    {
        namespace {
            inline void wait(int ms)
            {
                QEventLoop loop;
                QTimer timer;
                timer.setSingleShot(true);
                timer.setInterval(ms);
                QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
                timer.start();
                loop.exec();
            }

            cv::Mat rangeMapToMat(Core::RangeMapPtr rmp)
            {
                cv::Mat res(rmp->rows(), rmp->cols(), CV_8UC1, rmp->matrix());
                return res;
            }
        }



        LTCameraDriver* LTCameraDriver::instance()
        {
            if(PLScannerCore::LT360X::instance()->driver() == "Fake")
            {
                static LTFakeCameraDriver driver;
                return &driver;
            }
            else if(PLScannerCore::LT360X::instance()->driver() == "DHCamera")
            {
                static LTDHCameraDriver driver;
                return &driver;
            }
            else
            {
                OiWarning() << "Camera NULL " << PLScannerCore::LT360X::instance()->driver();
                return NULL;
            }
        }

        struct  CallbackData
        {
            void* ptr;
            QString name;
        };

        class FakeCameraThread : public QThread
        {
        public:
            FakeCameraThread(int num): cameraNum(num)
            {
                flushFomula();
                
            }

            ~FakeCameraThread()
            {
            }

        public:
            void run() override
            {
                Base::setThreadName(0, "FakeCamera");
                while (true)
                {
                    if (!(_run || _snap))
                    {
                        continue;
                    }

                    if (!_callback)
                    {
                        continue;
                    }

                    QString root("../Data/Scanner/%1/OI_0%2/%3.mat");

                    QMap<QString, Core::RangeMapPtr> frame;
                    const auto& cameraNames = LT360X::instance()->cameraNames;
                    bool ready = true;
                    for (int i = 0; i < cameraNames.size(); i++)
                    {
                        QString fp = root.arg(ProductParams::current()).arg(i + 1).arg(_currentFrame);
                        if (QFileInfo(fp).exists())
                        {
                            Core::RangeMapPtr rm = Core::RangeMap::create(fp);
                            frame[cameraNames[i]] = rm;
                        }
                        else
                        {
                            ready = false;
                        }
                    }

                    _callback(frame, QDateTime::currentDateTime(), _data);

                    _currentFrame = _dataSize != 0 ? (_currentFrame + 1) % _dataSize : _currentFrame + 1;
                    _snap = false;
                    QThread::msleep(500);
                }
            }

            void startCamera()
            {
                QMutexLocker lock(&_mutex);
                _run = true;
            }

            void stopCamera()
            {
                QMutexLocker lock(&_mutex);
                _run = false;
            }

            void snap()
            {
                QMutexLocker lock(&_mutex);
                _snap = true;
            }

            void setCallback(LTCameraCallback callback, void* data)
            {
                _callback = callback;
                _data = data;
            }

            void flushFomula()
            {
                int minSz = INT_MAX;
                QString strPath(QString("../Data/Scanner/%1/OI_0%2"));
                for (int i = 1; i <= LT360X::instance()->cameraNames.size(); ++i)
                {
                    QDir dir(strPath.arg(ProductParams::current()).arg(i));
                    dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
                    int cnt = dir.count();
                    if (cnt < minSz)
                    {
                        minSz = cnt;
                    }
                }
                _currentFrame = 0;
                _dataSize = minSz == INTMAX_MAX ? 0 : minSz;
            }

        public:
            bool _run = false;
            bool _snap = false;

            LTCameraCallback _callback = NULL;
            void* _data;
            int _currentFrame = 0;
            int _dataSize;
            QMutex _mutex;

            int cameraNum;

        };

        class LTFakeCameraDriverImpl : public Impl<LTFakeCameraDriver>
        {

        public:
            void init();
            void exit();
            void restart();
        public:
            QMap<QString, QList<Core::RangeMapPtr>> _rangeMaps;
            FakeCameraThread* _cameraThread;
            QMap<QString, Core::Calib3DPtr> _cabPtrs;
        };

        void LTFakeCameraDriverImpl::init()
        {
            OI_Q(LTFakeCameraDriver);

            int capType = Core::OiSettings()->value("CaptureType", 1).toInt();

            if (capType == 2)
            {
                for (int i = 1; i <= 8; ++i)
                {
                    QString name = QString("OI_0%1").arg(i);
                    QString cabFile = QCoreApplication::applicationDirPath() + "/" + "calibs" + "/" + name + ".ini";
                    _cabPtrs[name] = Core::Calib3D::create();
                    _cabPtrs[name]->setInformation(cabFile.toStdString());
                }
            }
            _cameraThread = new FakeCameraThread(capType == 1 ? 4 : 8);
            _cameraThread->start();
        }

        void LTFakeCameraDriverImpl::exit()
        {
            _cameraThread->quit();
            _cameraThread->deleteLater();
        }

        void LTFakeCameraDriverImpl::restart()
        {
            QMetaObject::invokeMethod(qApp->property("mainWindow").value<QWidget*>(), "onReStartCamera", Qt::QueuedConnection);
        }

        LTFakeCameraDriver::LTFakeCameraDriver()
        {
            OI_F(LTFakeCameraDriver);
            d->init();
            OiWarning() << "Camera Fake";
        }

        LTFakeCameraDriver::~LTFakeCameraDriver()
        {
            OI_E(LTFakeCameraDriver)->exit();
        }

        void LTFakeCameraDriver::startCameraTimer()
        {
        }

        void LTFakeCameraDriver::finishCameraTimer()
        {
        }

        void LTFakeCameraDriver::cameraTimeout()
        {
        }

        void LTFakeCameraDriver::setCallback(LTCameraCallback callback, void* data)
        {
            OI_F(LTFakeCameraDriver);
            d->_cameraThread->setCallback(callback, data);
        }

        void LTFakeCameraDriver::start()
        {
            OI_F(LTFakeCameraDriver);
            d->_cameraThread->startCamera();
        }

        void LTFakeCameraDriver::close()
        {

        }

        void LTFakeCameraDriver::stop()
        {
            OI_F(LTFakeCameraDriver);
            d->_cameraThread->stopCamera();
        }

        void LTFakeCameraDriver::snap()
        {
            OI_F(LTFakeCameraDriver);
            d->_cameraThread->snap();
        }

        void LTFakeCameraDriver::setMaxROI()
        {
        }

        void LTFakeCameraDriver::setOffsetX(QString cameraName, int value)
        {
        }

        void LTFakeCameraDriver::setOffsetY(QString cameraName, int value)
        {
        }

        void LTFakeCameraDriver::setWidth(QString cameraName, int value)
        {
        }

        void LTFakeCameraDriver::setHeight(QString cameraName, int value)
        {
        }

        int LTFakeCameraDriver::offsetX(QString cameraName)
        {
            return 0;
        }

        int LTFakeCameraDriver::offsetY(QString cameraName)
        {
            return 0;
        }

        void LTFakeCameraDriver::restartCamera()
        {
        }

        void LTFakeCameraDriver::setExposure(int)
        {
        }
        
        void LTFakeCameraDriver::setGain(double value)
        {

        }

        Math::Point2d LTFakeCameraDriver::calibrationTransformSection(const QString& camName, const Math::Point2d& source)
        {
            OI_F(LTFakeCameraDriver);
            Math::Point2d res = source;
            if (!ThreadSafeMapOperator(d->_cabPtrs, camName).isNull())
            {
                ThreadSafeMapOperator(d->_cabPtrs, camName)->calibrationTransformSection(source, 0, 0, res);
            }
            else
            {
                OiWarning() << "Calib is null: " << camName;
            }
            return res;
        }

        void LTFakeCameraDriver::flushFormula()
        {
            OI_F(LTFakeCameraDriver);
            d->_cameraThread->flushFomula();
        }

        class LTDHCameraDriverImpl : public Impl<LTDHCameraDriver>
        {
        public:
            bool init();
            void exit();

        public:
            QMap<QString, Camera::Device*> _devices;
            QMap<QString, QList<Core::RangeMapPtr>> _rangeMaps;
            QMap<QString, QList<qint64>> _timestamps;

            QMap<QString, CallbackData*> _callbackDatas;
            QList<QString> _cameraNames;
            QString _mainCamera;
            QMap<QString, int> _offsetX;
            QMap<QString, int> _offsetY;
            QMap<QString, int> _width;
            QMap<QString, int> _height;
            QMap<QString, QString> _devGuids;
            QMap<QString, QString> _cabFiles;
            QMap<QString, Core::Calib3DPtr> _cabPtrs;

            bool _snap = false;

        public:
            std::atomic_bool _manulStop;
            bool _restarting;

            int _realTimeCount;
            QTimer* _cameraState;

            LTCameraCallback _callback;
            void* _data;
            QMutex _mutex;
            bool _initSuccess;

        public:
            void restart();

        public:
            static void refresh(const Camera::Frame& frame, void* data);
        };

        bool LTDHCameraDriverImpl::init()
        {
            auto driver = Camera::DriverManager::instance()->driver("DHCamera");
            if (driver == NULL)
            {
                OiWarning() << "相机驱动未安装！";
                return false;
            }

            auto devices = driver->devices();

            _cameraNames = PLScannerCore::LT360X::instance()->cameraNames;
            if (!_cameraNames.isEmpty())
            {
                _mainCamera = _cameraNames[0];
            }
            else
            {
                OiWarning() << "_cameraNames 为空";
                return false;
            }

            if (devices.size() < _cameraNames.size())
            {
                OiAlerter() << "相机加载失败, 只获取到" << devices.size() << "个相机";
            }

            for (auto& name : _cameraNames)
            {
                bool found = false;
                for (auto device : devices)
                {
                    if (name == device->objectName())
                    {
                        _devices[name] = device;
                        auto data = new CallbackData;
                        data->name = name;
                        data->ptr = static_cast<void*>(this);
                        _callbackDatas[name] = data;
                        found = true;
                        _devGuids[name] = device->guid();
                        const QString& devGuid = _devGuids[name];
                        if (!devGuid.isEmpty())
                        {
                            OiWarning() << "Camera key: " << name << " - " << devGuid;
                            _cabFiles[name] = QCoreApplication::applicationDirPath() + "/" + "calibs" + "/" + name + ".ini";
                            OiWarning() << _cabFiles[name] << " Loaded";
                            _cabPtrs[name] = Core::Calib3D::create();
                            _cabPtrs[name]->setInformation(_cabFiles[name].toStdString());
                        }
                    }
                }
                if (!found)
                {
                    OiWarning() << "缺失相机: " << name;
                    OiAlerter() << "缺失相机："<<name;
                    return false;
                }
            }

            return true;
        }

        void LTDHCameraDriverImpl::exit()
        {
            for (auto data : _callbackDatas)
            {
                delete data;
            }
        }

        void LTDHCameraDriverImpl::restart()
        {
            OI_Q(LTDHCameraDriver);
            QMetaObject::invokeMethod(qApp->property("mainWindow").value<QWidget*>(), "onReStartCamera", Qt::QueuedConnection);
        }

        void LTDHCameraDriverImpl::refresh(const Camera::Frame& frame, void* data)
        {
//            StopWatch timer("LaserExtraction:");
            auto* mData = static_cast<CallbackData*>(data);
            auto self = static_cast<LTDHCameraDriverImpl*>(mData->ptr);
            auto name = mData->name;
            auto image = Core::Image::create(Core::Image::IMG, reinterpret_cast<QImage*>(frame.image), 0, 0);

            auto time = QDateTime::currentDateTime();
            cv::Mat source = image->to<cv::Mat>(Core::Image::MAT);
            
            if (PLScannerCore::LT360X::instance()->imageLogging())
            {
                QString appStr = QCoreApplication::applicationDirPath();
                QDir appDir(appStr);
                appDir.cdUp();
                appStr = appDir.absolutePath();
                QString imgDir = QString("%1/Data/SCamera/%2").arg(appStr).arg(name);
                QDir dir(imgDir);
                if (!dir.exists())
                {
                    dir.mkpath(imgDir);
                    OiWarning() << "创建目录：" << imgDir;
                }
                QString imgName = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz") + ".bmp";
                QString imgPath = imgDir + "/" + imgName;
                cv::imwrite(imgPath.toStdString(), source);
                OiWarning() << "保存文件：" << imgPath;
            }

            int rows = 1;
            auto range = Core::RangeMap::create(source.cols, rows);
            int _laserThreshold = PLScannerCore::LT360X::instance()->laserThreshold();
            for (int i = 0; i < rows; i++)
            {
                range->newProfile();
            }
            for (int i = 0; i < source.cols; i++)
            {
                int max = -1;
                int maxLocation = 0;
                for (int j = 0; j < source.rows; j++)
                {
                    if (max < source.ptr(j)[i])
                    {
                        maxLocation = j;
                        max = source.ptr(j)[i];
                    }
                }

                if (max < _laserThreshold)
                {
                    continue;
                }

                double threshold = 0.5 * max;

                float num = 0;
                float denum = 0;

                int width = 0;

                for (int upIter = maxLocation; upIter > -1; upIter--)
                {
                    if (source.ptr(upIter)[i] < threshold)
                    {
                        break;
                    }
                    num = num + upIter * source.ptr(upIter)[i];
                    denum = denum + source.ptr(upIter)[i];
                    width++;
                }

                for (int downIter = maxLocation + 1; downIter < source.rows; downIter++)
                {
                    if (source.ptr(downIter)[i] < threshold)
                    {
                        break;
                    }
                    num = num + downIter * source.ptr(downIter)[i];
                    denum = denum + source.ptr(downIter)[i];
                    width++;
                }

                if (PLScannerCore::LT360X::instance()->laserWidthThreshold() > 0 && width > PLScannerCore::LT360X::instance()->laserWidthThreshold())
                {
                    continue;
                }

                if (denum > 0)
                {
                    double x = i;
                    double y = num / denum;

                    for (int r = 0; r < rows; r++)
                    {
                        range->profile(r)[i] = y;
                    }
                    
                }
            }
            self->_mutex.lock();

            if (name == self->_mainCamera)
            {
                self->_realTimeCount++;
            }

            //auto* mData = static_cast<CallbackData*>(data);
            //auto self = static_cast<LTDHCameraDriverImpl*>(mData->ptr);
            //auto name = mData->name;
            //if (!frame.rm)
            //{
            //    return;
            //}
            //auto range = *static_cast<Core::RangeMapPtr*>(frame.rm);

            //self->_mutex.lock();

            //if (name == self->_mainCamera)
            //{
            //    self->_realTimeCount++;
            //}

            if (self->_restarting)
            {
                self->_mutex.unlock();
                return;
            }

            //auto time = QDateTime::currentDateTime();
            self->_rangeMaps[name].append(range);
            self->_timestamps[name].append(time.toMSecsSinceEpoch());
            //OiWarning() << "Current camera name :" << name;
            bool ready = true;
            bool restart = false;
            for (const auto& cameraName : self->_cameraNames)
            {
                if (ThreadSafeMapOperator(self->_rangeMaps, cameraName).isEmpty())
                {
                    //OiWarning() << cameraName << " range map is empty";
                    ready = false;
                }
                if (ThreadSafeMapOperator(self->_rangeMaps, cameraName).size() > 10)
                {
                    for (const auto& cameraName2 : self->_cameraNames)
                    {
                        int sz = ThreadSafeMapOperator(self->_rangeMaps, cameraName2).size();
                        if (sz < 5)
                        {
                            OiWarning() << cameraName2 << " 掉线：" << sz;
                        }
                    }
                    restart = true;
                    break;
                }
            }

            if (restart)
            {
                // self->_restarting = true;
                self->_mutex.unlock();
                self->restart();
                return;
            }
            //OiWarning() << name << " ready:" << ready;
            if (ready)
            {
                QMap<QString, Core::RangeMapPtr> rangeMaps;
                QMap<QString, qint64> timestamps;

                for (const auto& cameraName : self->_cameraNames)
                {
                    rangeMaps[cameraName] = ThreadSafeMapOperator(self->_rangeMaps, cameraName)[0];
                    timestamps[cameraName] = ThreadSafeMapOperator(self->_timestamps, cameraName)[0];
                    self->_rangeMaps[cameraName].pop_front();
                    self->_timestamps[cameraName].pop_front();
                }

                qint64 mintime = timestamps[self->_mainCamera];
                qint64 maxtime = timestamps[self->_mainCamera];
                for (auto cameraName : self->_cameraNames)
                {
                    if (mintime > timestamps[cameraName]) mintime = timestamps[cameraName];
                    if (maxtime < timestamps[cameraName]) maxtime = timestamps[cameraName];
                }

                if (maxtime - mintime > PLScannerCore::LT360X::instance()->cameraSyncThreshold())
                {
                    OiWarning() << "camera not in sync! " << maxtime - mintime;
                    // self->_restarting = true;
                    self->_mutex.unlock();
                    self->restart();
                    return;
                }

                if (PLScannerCore::LT360X::instance()->matLogging())
                {
                    QString appStr = QCoreApplication::applicationDirPath();
                    QDir appDir(appStr);
                    appDir.cdUp();
                    appStr = appDir.absolutePath();
                    auto keyArr = rangeMaps.keys();
                    for (auto& k : keyArr)
                    {
                        QString imgDir = QString("%1/Data/Scanner/%2/%3").arg(appStr).arg(ProductParams::current()).arg(k);
                        QDir dir(imgDir);
                        if (!dir.exists())
                        {
                            dir.mkpath(imgDir);
                            OiWarning() << "创建RangeMap目录：" << imgDir;
                        }
                        dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
                        int cnt = dir.count();
                        Core::RangeMapPtr ptr = ThreadSafeMapOperator(rangeMaps, k);
                        ptr->save(imgDir + "/" + QString::number(cnt) + ".mat");
                    }
                }

                self->_callback(rangeMaps, QDateTime::currentDateTime(), self->_data);
            }
            self->_mutex.unlock();
        }

        LTDHCameraDriver::LTDHCameraDriver()
        {
            OI_F(LTDHCameraDriver);
            OiWarning() << "Camera DHCamera";
            d->_manulStop = false;
            d->_restarting = false;

            d->_realTimeCount = 0;
            d->_cameraState = new QTimer(this);
            d->_cameraState->setSingleShot(true);
            d->_cameraState->setInterval(1000);
            QObject::connect(d->_cameraState, &QTimer::timeout, this, &LTDHCameraDriver::cameraTimeout);

            d->_initSuccess = d->init();
        }

        LTDHCameraDriver::~LTDHCameraDriver()
        {
            OI_E(LTDHCameraDriver)->exit();
        }

        void LTDHCameraDriver::startCameraTimer()
        {
            OI_IMPL()->_cameraState->start();
        }

        void LTDHCameraDriver::finishCameraTimer()
        {
            OI_IMPL()->_cameraState->stop();
        }

        void LTDHCameraDriver::cameraTimeout()
        {
            OI_F(LTDHCameraDriver);

            if (d->_realTimeCount > 0)
            {
                d->_cameraState->start();
            }
            else if (!d->_restarting)
            {
                OiWarning() << "主相机掉线";
                QtConcurrent::run(this, &LTDHCameraDriver::restartCamera);
            }
            d->_realTimeCount = 0;
        }

        void LTDHCameraDriver::setCallback(LTCameraCallback callback, void* data)
        {
            OI_F(LTDHCameraDriver);
            d->_callback = callback;
            d->_data = data;
        }

        void LTDHCameraDriver::start()
        {
            OI_F(LTDHCameraDriver);

            d->_snap = false;

            if (!d->_initSuccess) return;

            for (auto camera : d->_devices)
            {
                if (!camera->isOpen())
                {
                    camera->open();
                }

                if (camera->isOpen() && camera->isShot())
                {
                    camera->stopCapture();
                }
            }

            for (auto& range : d->_rangeMaps)
            {
                range.clear();
            }
            for (auto& time : d->_timestamps)
            {
                time.clear();
            }

            int indexCamera = 0;
            for (auto camera : d->_devices)
            {
                OiWarning() << "indexCamera:" << indexCamera;
                if (!camera->isOpen())
                {
                    camera->open();
                }
                indexCamera++;
                if (!camera->isOpen())
                {
                    OiWarning() << "open filed:"<< indexCamera;
                    continue;
                }

                if (camera->isShot())
                {
                    OiWarning() << "stopCapture:" << indexCamera;
                    camera->stopCapture();
                }

                camera->setProperty(Camera::Device::P_XOffset, 0);
                camera->setProperty(Camera::Device::P_YOffset, 0);
                camera->setProperty(Camera::Device::P_WOffset, d->_width[camera->objectName()]);
                camera->setProperty(Camera::Device::P_HOffset, d->_height[camera->objectName()]);
                camera->setProperty(Camera::Device::P_XOffset, d->_offsetX[camera->objectName()]);
                camera->setProperty(Camera::Device::P_YOffset, d->_offsetY[camera->objectName()]);

                camera->setProperty(Camera::Device::P_WorkMode, Camera::Device::WM_2DONLY);
                camera->setProperty(Camera::Device::P_TakeMode, Camera::Device::TM_Continuous);
                if (camera->objectName() == d->_mainCamera)
                {
                    OiWarning() << "Main Camera: " << camera->objectName();
                    camera->setProperty(Camera::Device::P_FTrigger, 0);
                }
                else
                {
                    camera->setProperty(Camera::Device::P_FTrigger, 1);
                }

                camera->setCallback(LTDHCameraDriverImpl::refresh, d->_callbackDatas[camera->objectName()]);

                if (camera->objectName() != d->_mainCamera)
                {
                    camera->live();
                }
            }

            wait(500);

            bool ready = true;

            for (auto camera : d->_devices)
            {
                if (camera->objectName() != d->_mainCamera)
                {
                    if (!camera->isTake())
                    {
                        ready = false;
                    }
                }
            }

            if (d->_manulStop)
            {
                for (auto camera : d->_devices)
                {
                    camera->stopCapture();
                }
            }
            else if (ready && d->_devices[d->_mainCamera]->isOpen())
            {
                QMetaObject::invokeMethod(this, "startCameraTimer", Qt::QueuedConnection);
                d->_devices[d->_mainCamera]->live();
            }
            else
            {
                restartCamera();
            }
        }

        void LTDHCameraDriver::close()
        {
            OI_F(LTDHCameraDriver);
            if (!d->_initSuccess) return;

            QMetaObject::invokeMethod(this, "finishCameraTimer", Qt::QueuedConnection);

            for (auto camera : d->_devices)
            {
                if (camera->isOpen())
                {
                    camera->close();
                }
            }
        }

        void LTDHCameraDriver::stop()
        {
            OI_F(LTDHCameraDriver);
            if (!d->_initSuccess) return;

            QMetaObject::invokeMethod(this, "finishCameraTimer", Qt::QueuedConnection);

            d->_manulStop = true;
            while (d->_restarting)
            {
                wait(100);
            }
            d->_manulStop = false;

            for (auto camera : d->_devices)
            {
                if (camera->isOpen())
                {
                    camera->stopCapture();
                }
            }
        }

        void LTDHCameraDriver::snap()
        {
            OI_F(LTDHCameraDriver);
            d->_snap = true;

            if (!d->_initSuccess)
            {
                OiAlerter() << "相机初始化失败";
                return;
            }
            if (!d->_initSuccess)
            {
                Camera::DriverManager::instance()->driver("PVCamera")->invalidate();
                OiAlerter() << "相机初始化失败1";
                d->_initSuccess = d->init();
            }

            if (!d->_initSuccess)
            {
                OiAlerter() << "相机初始化失败2";
                return;
            }

            for (auto camera : d->_devices)
            {
                if (!camera->isOpen())
                {
                    camera->open();
                }

                if (camera->isOpen() && camera->isShot())
                {
                    camera->stopCapture();
                }
            }

            for (auto& range : d->_rangeMaps)
            {
                range.clear();
            }
            for (auto& time : d->_timestamps)
            {
                time.clear();
            }
            int indexCamera = 0;
            for (auto camera : d->_devices)
            {
                indexCamera++;
                if (!camera->isOpen())
                {
                    camera->open();
                }

                if (!camera->isOpen())
                {
                    OiWarning() << "open filed:" << indexCamera;
                    continue;
                }

                if (camera->isShot())
                {
                    OiWarning() << "stopCapture:" << indexCamera;
                    camera->stopCapture();
                }

                setExposure(PLScannerCore::LT360X::instance()->exposureTime());
                setGain(PLScannerCore::LT360X::instance()->gain());
                camera->setProperty(Camera::Device::P_XOffset, 0);
                camera->setProperty(Camera::Device::P_YOffset, 0);
                camera->setProperty(Camera::Device::P_WOffset, d->_width[camera->objectName()]);
                camera->setProperty(Camera::Device::P_HOffset, d->_height[camera->objectName()]);
                camera->setProperty(Camera::Device::P_XOffset, d->_offsetX[camera->objectName()]);
                camera->setProperty(Camera::Device::P_YOffset, d->_offsetY[camera->objectName()]);

                if (camera->objectName() == d->_mainCamera)
                {
                    camera->setProperty(Camera::Device::P_FTrigger, 0);
                }
                else
                {
                    camera->setProperty(Camera::Device::P_FTrigger, 1);
                }

                camera->setProperty(Camera::Device::P_WorkMode, Camera::Device::WM_2DONLY);
                camera->setProperty(Camera::Device::P_TakeMode, Camera::Device::TM_SingleFrame);

                camera->setCallback(LTDHCameraDriverImpl::refresh, d->_callbackDatas[camera->objectName()]);

                if (camera->objectName() != d->_mainCamera)
                {
                    OiWarning() << "snap indexCamera:" << indexCamera;
                    camera->snap();
                }
            }

            wait(500);

            bool ready = true;

            for (auto camera : d->_devices)
            {
                if (camera->objectName() != d->_mainCamera)
                {
                    if (!camera->isTake())
                    {
                        ready = false;
                    }
                }
            }

            if (ready && d->_devices[d->_mainCamera]->isOpen())
            {
                d->_devices[d->_mainCamera]->snap();
            }
        }

        void LTDHCameraDriver::setMaxROI()
        {
            OI_F(LTDHCameraDriver);
            if (!d->_devices[d->_mainCamera])
            {
                OiWarning() << "相机未连接！";
            }
            else
            {
                if (!d->_devices[d->_mainCamera]->isOpen())
                {
                    d->_devices[d->_mainCamera]->open();
                }

                for (auto& name : PLScannerCore::LT360X::instance()->cameraNames)
                {
                    d->_offsetX[name] = 0;
                    d->_offsetY[name] = 0;

                    d->_width[name] = d->_devices[d->_mainCamera]->property(Camera::Device::P_WMaximize);
                    d->_height[name] = d->_devices[d->_mainCamera]->property(Camera::Device::P_HMaximize);
                }
            }
        }

        void LTDHCameraDriver::setOffsetX(QString cameraName, int value)
        {
            //OI_IMPL()->_offsetX[cameraName] = value;
        }

        void LTDHCameraDriver::setOffsetY(QString cameraName, int value)
        {
            //OI_IMPL()->_offsetY[cameraName] = value;
        }

        void LTDHCameraDriver::setWidth(QString cameraName, int value)
        {
            //OI_IMPL()->_width[cameraName] = value;
        }

        void LTDHCameraDriver::setHeight(QString cameraName, int value)
        {
            //OI_IMPL()->_height[cameraName] = value;
        }

        int LTDHCameraDriver::offsetX(QString cameraName)
        {
            return OI_IMPL()->_offsetX[cameraName];
        }

        int LTDHCameraDriver::offsetY(QString cameraName)
        {
            return OI_IMPL()->_offsetY[cameraName];
        }

        void LTDHCameraDriver::restartCamera()
        {
            OI_F(LTDHCameraDriver);
            d->_restarting = true;
            close();
            for (auto& list : d->_rangeMaps)
            {
                list.clear();
            }
            for (auto& times : d->_timestamps)
            {
                times.clear();
            }

            if (!d->_snap && !d->_manulStop)
            {
                start();
            }

            d->_restarting = false;
        }

        void LTDHCameraDriver::setExposure(int value)
        {
            OI_F(LTDHCameraDriver);
            for (auto camera : d->_devices)
            {
                camera->setProperty(Camera::Device::P_ExposureTime, value);
            }
        }
        
        void LTDHCameraDriver::setGain(double value)
        {
            OI_F(LTDHCameraDriver);

            QString msg = QString("[{\"name\": \"GainRaw\",  \"type\": \"double\" ,  \"value\": %1}]").arg(value);
            QObject params;
            params.setProperty("extras", Core::Value(msg));
            QPair<int, QObject*> pair(0, &params);
            for (auto camera : d->_devices)
            {
                camera->initialize(&pair);
            }
        }

        Math::Point2d LTDHCameraDriver::calibrationTransformSection(const QString& camName, const Math::Point2d& source)
        {
            OI_F(LTDHCameraDriver);

            Math::Point2d res = source;
            if (!ThreadSafeMapOperator(d->_cabPtrs, camName).isNull())
            {
                ThreadSafeMapOperator(d->_cabPtrs, camName)->calibrationTransformSection(source, 0, 0, res);
            }
            else
            {
                OiWarning() << "Calib is null: " << camName << " - " << d->_cabFiles;
            }
            return res;
        }

        void LTDHCameraDriver::flushFormula()
        {
        }

    }
}